﻿namespace UnityEngine
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    public class AndroidJavaObject : AndroidJNIHelper, IDisposable
    {
        private static bool enableDebugPrints;
        private bool m_disposed;
        protected Dictionary<int, IntPtr> m_fieldIDs;
        protected IntPtr m_jclass;
        protected IntPtr m_jobject;
        protected Dictionary<int, IntPtr> m_methodIDs;

        protected AndroidJavaObject()
        {
            this.m_methodIDs = new Dictionary<int, IntPtr>();
            this.m_fieldIDs = new Dictionary<int, IntPtr>();
        }

        public AndroidJavaObject(IntPtr jobject) : this()
        {
            if (jobject == IntPtr.Zero)
            {
                Debug.LogWarning("JNI: Init'd AndroidJavaObject with null ptr!");
            }
            else
            {
                this.m_jobject = AndroidJNI.NewGlobalRef(jobject);
                this.m_jclass = AndroidJNI.NewGlobalRef(AndroidJNI.GetObjectClass(this.m_jobject));
            }
        }

        public AndroidJavaObject(string className, params object[] args) : this()
        {
            this._AndroidJavaObject(className, args);
        }

        private void _AndroidJavaObject(string className, params object[] args)
        {
            this.DebugPrint("Creating AndroidJavaObject from " + className);
            if (args == null)
            {
                args = new object[1];
            }
            className = className.Replace('.', '/');
            IntPtr ptr = AndroidJNI.FindClass(className);
            this.m_jclass = AndroidJNI.NewGlobalRef(ptr);
            IntPtr constructorID = AndroidJNIHelper.GetConstructorID(ptr, args);
            jvalue[] jvalueArray = AndroidJNIHelper.CreateJNIArgArray(args);
            IntPtr ptr3 = AndroidJNI.NewObject(ptr, constructorID, jvalueArray);
            this.m_jobject = AndroidJNI.NewGlobalRef(ptr3);
        }

        protected ReturnType _Call<ReturnType>(string methodName, params object[] args)
        {
            if (args == null)
            {
                args = new object[1];
            }
            string signature = AndroidJNIHelper.GetSignature<ReturnType>(args);
            this.DebugPrint("Call<" + typeof(ReturnType).ToString() + ">", methodName, signature, args);
            IntPtr methodID = this.GetCachedMethodID<ReturnType>(methodName, args, false);
            jvalue[] jvalueArray = AndroidJNIHelper.CreateJNIArgArray(args);
            if (typeof(ReturnType).IsPrimitive)
            {
                if (typeof(ReturnType) == typeof(int))
                {
                    return (ReturnType) AndroidJNI.CallIntMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(bool))
                {
                    return (ReturnType) AndroidJNI.CallBooleanMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(byte))
                {
                    return (ReturnType) AndroidJNI.CallByteMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(short))
                {
                    return (ReturnType) AndroidJNI.CallShortMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(long))
                {
                    return (ReturnType) AndroidJNI.CallLongMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(float))
                {
                    return (ReturnType) AndroidJNI.CallFloatMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(double))
                {
                    return (ReturnType) AndroidJNI.CallDoubleMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(char))
                {
                    return (ReturnType) AndroidJNI.CallCharMethod(this.m_jobject, methodID, jvalueArray);
                }
            }
            else
            {
                if (typeof(ReturnType) == typeof(string))
                {
                    return (ReturnType) AndroidJNI.CallStringMethod(this.m_jobject, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(AndroidJavaClass))
                {
                    return (ReturnType) new AndroidJavaClass(AndroidJNI.CallObjectMethod(this.m_jobject, methodID, jvalueArray));
                }
                if (typeof(ReturnType) == typeof(AndroidJavaObject))
                {
                    return (ReturnType) new AndroidJavaObject(AndroidJNI.CallObjectMethod(this.m_jobject, methodID, jvalueArray));
                }
                if (typeof(Array).IsAssignableFrom(typeof(ReturnType)))
                {
                    return AndroidJNIHelper.ConvertFromJNIArray<ReturnType>(AndroidJNI.CallObjectMethod(this.m_jobject, methodID, jvalueArray));
                }
                Debug.LogError("JNI: Unknown return type '" + typeof(ReturnType) + "'");
            }
            return default(ReturnType);
        }

        protected void _Call(string methodName, params object[] args)
        {
            if (args == null)
            {
                args = new object[1];
            }
            string signature = AndroidJNIHelper.GetSignature(args);
            this.DebugPrint("Call<void>", methodName, signature, args);
            IntPtr methodID = this.GetCachedMethodID(methodName, args, false);
            jvalue[] jvalueArray = AndroidJNIHelper.CreateJNIArgArray(args);
            AndroidJNI.CallVoidMethod(this.m_jobject, methodID, jvalueArray);
        }

        protected ReturnType _CallStatic<ReturnType>(string methodName, params object[] args)
        {
            if (args == null)
            {
                args = new object[1];
            }
            string signature = AndroidJNIHelper.GetSignature<ReturnType>(args);
            this.DebugPrint("CallStatic<" + typeof(ReturnType).ToString() + ">", methodName, signature, args);
            IntPtr methodID = this.GetCachedMethodID<ReturnType>(methodName, args, true);
            jvalue[] jvalueArray = AndroidJNIHelper.CreateJNIArgArray(args);
            if (typeof(ReturnType).IsPrimitive)
            {
                if (typeof(ReturnType) == typeof(int))
                {
                    return (ReturnType) AndroidJNI.CallStaticIntMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(bool))
                {
                    return (ReturnType) AndroidJNI.CallStaticBooleanMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(byte))
                {
                    return (ReturnType) AndroidJNI.CallStaticByteMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(short))
                {
                    return (ReturnType) AndroidJNI.CallStaticShortMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(long))
                {
                    return (ReturnType) AndroidJNI.CallStaticLongMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(float))
                {
                    return (ReturnType) AndroidJNI.CallStaticFloatMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(double))
                {
                    return (ReturnType) AndroidJNI.CallStaticDoubleMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(char))
                {
                    return (ReturnType) AndroidJNI.CallStaticCharMethod(this.m_jclass, methodID, jvalueArray);
                }
            }
            else
            {
                if (typeof(ReturnType) == typeof(string))
                {
                    return (ReturnType) AndroidJNI.CallStaticStringMethod(this.m_jclass, methodID, jvalueArray);
                }
                if (typeof(ReturnType) == typeof(AndroidJavaClass))
                {
                    return (ReturnType) new AndroidJavaClass(AndroidJNI.CallStaticObjectMethod(this.m_jclass, methodID, jvalueArray));
                }
                if (typeof(ReturnType) == typeof(AndroidJavaObject))
                {
                    return (ReturnType) new AndroidJavaObject(AndroidJNI.CallStaticObjectMethod(this.m_jclass, methodID, jvalueArray));
                }
                if (typeof(Array).IsAssignableFrom(typeof(ReturnType)))
                {
                    return AndroidJNIHelper.ConvertFromJNIArray<ReturnType>(AndroidJNI.CallStaticObjectMethod(this.m_jclass, methodID, jvalueArray));
                }
                Debug.LogError("JNI: Unknown return type '" + typeof(ReturnType) + "'");
            }
            return default(ReturnType);
        }

        protected void _CallStatic(string methodName, params object[] args)
        {
            if (args == null)
            {
                args = new object[1];
            }
            string signature = AndroidJNIHelper.GetSignature(args);
            this.DebugPrint("CallStatic<void>", methodName, signature, args);
            IntPtr methodID = this.GetCachedMethodID(methodName, args, true);
            jvalue[] jvalueArray = AndroidJNIHelper.CreateJNIArgArray(args);
            AndroidJNI.CallStaticVoidMethod(this.m_jclass, methodID, jvalueArray);
        }

        protected void _Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected FieldType _Get<FieldType>(string fieldName)
        {
            IntPtr cachedFieldID = this.GetCachedFieldID<FieldType>(fieldName, false);
            if (typeof(FieldType).IsPrimitive)
            {
                if (typeof(FieldType) == typeof(int))
                {
                    return (FieldType) AndroidJNI.GetIntField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(bool))
                {
                    return (FieldType) AndroidJNI.GetBooleanField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(byte))
                {
                    return (FieldType) AndroidJNI.GetByteField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(short))
                {
                    return (FieldType) AndroidJNI.GetShortField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(long))
                {
                    return (FieldType) AndroidJNI.GetLongField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(float))
                {
                    return (FieldType) AndroidJNI.GetFloatField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(double))
                {
                    return (FieldType) AndroidJNI.GetDoubleField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(char))
                {
                    return (FieldType) AndroidJNI.GetCharField(this.m_jobject, cachedFieldID);
                }
            }
            else
            {
                if (typeof(FieldType) == typeof(string))
                {
                    return (FieldType) AndroidJNI.GetStringField(this.m_jobject, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(AndroidJavaClass))
                {
                    return (FieldType) new AndroidJavaClass(AndroidJNI.GetObjectField(this.m_jobject, cachedFieldID));
                }
                if (typeof(FieldType) == typeof(AndroidJavaObject))
                {
                    return (FieldType) new AndroidJavaObject(AndroidJNI.GetObjectField(this.m_jobject, cachedFieldID));
                }
                if (typeof(Array).IsAssignableFrom(typeof(FieldType)))
                {
                    return AndroidJNIHelper.ConvertFromJNIArray<FieldType>(AndroidJNI.GetObjectField(this.m_jobject, cachedFieldID));
                }
                Debug.LogError("JNI: Unknown field type '" + typeof(FieldType) + "'");
            }
            return default(FieldType);
        }

        protected IntPtr _GetRawClass()
        {
            return this.m_jclass;
        }

        protected IntPtr _GetRawObject()
        {
            return this.m_jobject;
        }

        protected FieldType _GetStatic<FieldType>(string fieldName)
        {
            IntPtr cachedFieldID = this.GetCachedFieldID<FieldType>(fieldName, true);
            if (typeof(FieldType).IsPrimitive)
            {
                if (typeof(FieldType) == typeof(int))
                {
                    return (FieldType) AndroidJNI.GetStaticIntField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(bool))
                {
                    return (FieldType) AndroidJNI.GetStaticBooleanField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(byte))
                {
                    return (FieldType) AndroidJNI.GetStaticByteField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(short))
                {
                    return (FieldType) AndroidJNI.GetStaticShortField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(long))
                {
                    return (FieldType) AndroidJNI.GetStaticLongField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(float))
                {
                    return (FieldType) AndroidJNI.GetStaticFloatField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(double))
                {
                    return (FieldType) AndroidJNI.GetStaticDoubleField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(char))
                {
                    return (FieldType) AndroidJNI.GetStaticCharField(this.m_jclass, cachedFieldID);
                }
            }
            else
            {
                if (typeof(FieldType) == typeof(string))
                {
                    return (FieldType) AndroidJNI.GetStaticStringField(this.m_jclass, cachedFieldID);
                }
                if (typeof(FieldType) == typeof(AndroidJavaClass))
                {
                    return (FieldType) new AndroidJavaClass(AndroidJNI.GetStaticObjectField(this.m_jclass, cachedFieldID));
                }
                if (typeof(FieldType) == typeof(AndroidJavaObject))
                {
                    return (FieldType) new AndroidJavaObject(AndroidJNI.GetStaticObjectField(this.m_jclass, cachedFieldID));
                }
                if (typeof(Array).IsAssignableFrom(typeof(FieldType)))
                {
                    return AndroidJNIHelper.ConvertFromJNIArray<FieldType>(AndroidJNI.GetStaticObjectField(this.m_jclass, cachedFieldID));
                }
                Debug.LogError("JNI: Unknown field type '" + typeof(FieldType) + "'");
            }
            return default(FieldType);
        }

        protected void _Set<FieldType>(string fieldName, FieldType val)
        {
            IntPtr cachedFieldID = this.GetCachedFieldID<FieldType>(fieldName, false);
            if (typeof(FieldType).IsPrimitive)
            {
                if (typeof(FieldType) == typeof(int))
                {
                    AndroidJNI.SetIntField(this.m_jobject, cachedFieldID, (int) val);
                }
                else if (typeof(FieldType) == typeof(bool))
                {
                    AndroidJNI.SetBooleanField(this.m_jobject, cachedFieldID, (bool) val);
                }
                else if (typeof(FieldType) == typeof(byte))
                {
                    AndroidJNI.SetByteField(this.m_jobject, cachedFieldID, (byte) val);
                }
                else if (typeof(FieldType) == typeof(short))
                {
                    AndroidJNI.SetShortField(this.m_jobject, cachedFieldID, (short) val);
                }
                else if (typeof(FieldType) == typeof(long))
                {
                    AndroidJNI.SetLongField(this.m_jobject, cachedFieldID, (long) val);
                }
                else if (typeof(FieldType) == typeof(float))
                {
                    AndroidJNI.SetFloatField(this.m_jobject, cachedFieldID, (float) val);
                }
                else if (typeof(FieldType) == typeof(double))
                {
                    AndroidJNI.SetDoubleField(this.m_jobject, cachedFieldID, (double) val);
                }
                else if (typeof(FieldType) == typeof(char))
                {
                    AndroidJNI.SetCharField(this.m_jobject, cachedFieldID, (char) val);
                }
            }
            else if (typeof(FieldType) == typeof(string))
            {
                AndroidJNI.SetStringField(this.m_jobject, cachedFieldID, (string) val);
            }
            else if (typeof(FieldType) == typeof(AndroidJavaClass))
            {
                AndroidJNI.SetObjectField(this.m_jobject, cachedFieldID, ((AndroidJavaClass) val).m_jclass);
            }
            else if (typeof(FieldType) == typeof(AndroidJavaObject))
            {
                AndroidJNI.SetObjectField(this.m_jobject, cachedFieldID, ((AndroidJavaObject) val).m_jobject);
            }
            else if (typeof(Array).IsAssignableFrom(typeof(FieldType)))
            {
                IntPtr ptr2 = AndroidJNIHelper.ConvertToJNIArray((Array) val);
                AndroidJNI.SetObjectField(this.m_jclass, cachedFieldID, ptr2);
            }
            else
            {
                Debug.LogError("JNI: Unknown field type '" + typeof(FieldType) + "'");
            }
        }

        protected void _SetStatic<FieldType>(string fieldName, FieldType val)
        {
            IntPtr cachedFieldID = this.GetCachedFieldID<FieldType>(fieldName, true);
            if (typeof(FieldType).IsPrimitive)
            {
                if (typeof(FieldType) == typeof(int))
                {
                    AndroidJNI.SetStaticIntField(this.m_jclass, cachedFieldID, (int) val);
                }
                else if (typeof(FieldType) == typeof(bool))
                {
                    AndroidJNI.SetStaticBooleanField(this.m_jclass, cachedFieldID, (bool) val);
                }
                else if (typeof(FieldType) == typeof(byte))
                {
                    AndroidJNI.SetStaticByteField(this.m_jclass, cachedFieldID, (byte) val);
                }
                else if (typeof(FieldType) == typeof(short))
                {
                    AndroidJNI.SetStaticShortField(this.m_jclass, cachedFieldID, (short) val);
                }
                else if (typeof(FieldType) == typeof(long))
                {
                    AndroidJNI.SetStaticLongField(this.m_jclass, cachedFieldID, (long) val);
                }
                else if (typeof(FieldType) == typeof(float))
                {
                    AndroidJNI.SetStaticFloatField(this.m_jclass, cachedFieldID, (float) val);
                }
                else if (typeof(FieldType) == typeof(double))
                {
                    AndroidJNI.SetStaticDoubleField(this.m_jclass, cachedFieldID, (double) val);
                }
                else if (typeof(FieldType) == typeof(char))
                {
                    AndroidJNI.SetStaticCharField(this.m_jclass, cachedFieldID, (char) val);
                }
            }
            else if (typeof(FieldType) == typeof(string))
            {
                AndroidJNI.SetStaticStringField(this.m_jclass, cachedFieldID, (string) val);
            }
            else if (typeof(FieldType) == typeof(AndroidJavaClass))
            {
                AndroidJNI.SetStaticObjectField(this.m_jclass, cachedFieldID, ((AndroidJavaClass) val).m_jclass);
            }
            else if (typeof(FieldType) == typeof(AndroidJavaObject))
            {
                AndroidJNI.SetStaticObjectField(this.m_jclass, cachedFieldID, ((AndroidJavaObject) val).m_jobject);
            }
            else if (typeof(Array).IsAssignableFrom(typeof(FieldType)))
            {
                IntPtr ptr2 = AndroidJNIHelper.ConvertToJNIArray((Array) val);
                AndroidJNI.SetStaticObjectField(this.m_jclass, cachedFieldID, ptr2);
            }
            else
            {
                Debug.LogError("JNI: Unknown field type '" + typeof(FieldType) + "'");
            }
        }

        public ReturnType Call<ReturnType>(string methodName, params object[] args)
        {
            return this._Call<ReturnType>(methodName, args);
        }

        public void Call(string methodName, params object[] args)
        {
            this._Call(methodName, args);
        }

        public ReturnType CallStatic<ReturnType>(string methodName, params object[] args)
        {
            return this._CallStatic<ReturnType>(methodName, args);
        }

        public void CallStatic(string methodName, params object[] args)
        {
            this._CallStatic(methodName, args);
        }

        protected void DebugPrint(string msg)
        {
            if (enableDebugPrints)
            {
                Debug.Log(msg);
            }
        }

        protected void DebugPrint(string call, string methodName, string signature, object[] args)
        {
            if (enableDebugPrints)
            {
                StringBuilder builder = new StringBuilder();
                foreach (object obj2 in args)
                {
                    builder.Append(", ");
                    builder.Append(obj2.GetType().ToString());
                }
                Debug.Log(call + "(\"" + methodName + "\"" + builder.ToString() + ") = " + signature);
            }
        }

        public void Dispose()
        {
            this._Dispose();
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.m_disposed)
            {
                this.m_disposed = true;
                if (this.m_jobject != IntPtr.Zero)
                {
                    AndroidJNI.DeleteGlobalRef(this.m_jobject);
                }
                if (this.m_jclass != IntPtr.Zero)
                {
                    AndroidJNI.DeleteGlobalRef(this.m_jclass);
                }
            }
        }

        ~AndroidJavaObject()
        {
            this.Dispose(true);
        }

        public FieldType Get<FieldType>(string fieldName)
        {
            return this._Get<FieldType>(fieldName);
        }

        protected IntPtr GetCachedFieldID<FieldType>(string fieldName, bool isStatic)
        {
            int key = fieldName.GetHashCode() ^ typeof(FieldType).ToString().GetHashCode();
            if (!this.m_fieldIDs.ContainsKey(key))
            {
                this.DebugPrint("field id not cached; doing lookup " + (!isStatic ? "(non-static)" : "(static)"));
                IntPtr ptr = AndroidJNIHelper.GetFieldID<FieldType>(this.m_jclass, fieldName, isStatic);
                this.m_fieldIDs.Add(key, ptr);
                return ptr;
            }
            this.DebugPrint("field id cached; skipping lookup; fid = " + this.m_fieldIDs[key]);
            return this.m_fieldIDs[key];
        }

        protected IntPtr GetCachedMethodID(string methodName, object[] args, bool isStatic)
        {
            int key = methodName.GetHashCode() ^ GetSignatureHash(args);
            if (!this.m_methodIDs.ContainsKey(key))
            {
                this.DebugPrint("method id not cached; doing lookup " + (!isStatic ? "(non-static)" : "(static)"));
                IntPtr ptr = AndroidJNIHelper.GetMethodID(this.m_jclass, methodName, args, isStatic);
                this.m_methodIDs.Add(key, ptr);
                return ptr;
            }
            this.DebugPrint("method id cached; skipping lookup; mid = " + this.m_methodIDs[key]);
            return this.m_methodIDs[key];
        }

        protected IntPtr GetCachedMethodID<ReturnType>(string methodName, object[] args, bool isStatic)
        {
            int key = methodName.GetHashCode() ^ GetSignatureHash<ReturnType>(args);
            if (!this.m_methodIDs.ContainsKey(key))
            {
                this.DebugPrint("method id not cached; doing lookup " + (!isStatic ? "(non-static)" : "(static)"));
                IntPtr ptr = AndroidJNIHelper.GetMethodID<ReturnType>(this.m_jclass, methodName, args, isStatic);
                this.m_methodIDs.Add(key, ptr);
                return ptr;
            }
            this.DebugPrint("method id cached; skipping lookup; mid = " + this.m_methodIDs[key]);
            return this.m_methodIDs[key];
        }

        public IntPtr GetRawClass()
        {
            return this._GetRawClass();
        }

        public IntPtr GetRawObject()
        {
            return this._GetRawObject();
        }

        protected static int GetSignatureHash(object[] args)
        {
            int num = 13;
            foreach (object obj2 in args)
            {
                num ^= obj2.GetType().GetHashCode();
            }
            return num;
        }

        protected static int GetSignatureHash<ReturnType>(object[] args)
        {
            return (typeof(ReturnType).GetHashCode() ^ GetSignatureHash(args));
        }

        public FieldType GetStatic<FieldType>(string fieldName)
        {
            return this._GetStatic<FieldType>(fieldName);
        }

        public void Set<FieldType>(string fieldName, FieldType val)
        {
            this._Set<FieldType>(fieldName, val);
        }

        public void SetStatic<FieldType>(string fieldName, FieldType val)
        {
            this._SetStatic<FieldType>(fieldName, val);
        }
    }
}

